package cn.tedu.v2_1;


import java.io.*;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Version  2.1  多人聊天室
 * 知识点：多线程并发
 * 实现步骤：1.Server类---创建多线程CS：目的：处理多个客户端发来的链接
 *          1.1找到Server类--主方法下：加入内部类多线程CS，
 *              目的：供本类任意地方使用
 *              结构：private class CS implements Runnable{}
 *           1.2在内部类多线程CS---私有化属性：Socket--表示线程中socket对
 *             在CS多线程类：private Socket socket;
 *          1.3创建构造器
 *           目的：通过线程创建--传入一个通过IP+端口号连接成功客户端socket
 *          public CS(socket s){this.socket=s;}
 *          参数：Socket s--start启动方法中：获取通过IP+端口
 *               返回正确客户端Socket
 *           理解：已经拨通服务器端的客户端
 *          语法块：this.socket=s;
 *          理解:当前线程中socket=获取通过IP+端口，返回正确客户端：
 *          1.4如果已经通过IP+端口号得到socket中的正确读入
 *           理解：来一个客户端---服务器端分配：专门socket.进行读入操作
 *          对应：CS线程中 run()  实现读入功能
 *          2.Server类，start方法：第一件事：无限次接收客户端
 *                               第二件事：利用线程--客户端分配正确链接
 *                                        进行读入操作
 */
public class Server {
     private ServerSocket serverSocket;
     public Server(){
        try{
             serverSocket=new ServerSocket(7878);
        } catch(BindException e){
            System.out.println("端口号重复申请，建议换个端口号");
        } catch (IOException e){
             System.out.println("申请端口号失败...");
        }
    }
    //二、服务器端启动start:第一件事：无限次接收客户端链接，返回一个客户端socket
    //                   第二件事：CS线程类：处理所有链接服务器端客户端
    //                              处理各个客户端的写入功能--【读取】
    public void start()  {


        try  {
            while(true) {
                //第一件事：无限次接受客户端链接，返回客户端socket
                System.out.println("-----等待客户端连接-----");
                Socket socket = serverSocket.accept();
                String ip = socket.getInetAddress().getHostAddress();
                System.out.println(ip + "一个客户端连接成功了");
              //第二件事：接收一个客户端链接--交给线程处理---读入功能
                //效果：连接上一个客户端---有一个服务器端、正确读入操作
                //多线程并发解决方法：来客户端--服务器使用线程--创建客户端链接
                 CS cs=new CS(socket);//1.通过链接，返回socket
                 Thread t1=new Thread(cs);//2.交给线程
                 t1.start();              //3.启动线程

            }
        } catch  (IOException e) {
             System.out.println("服务器端通讯断开了");
        }  finally {
        }

    }


     public static void main(String[] args) {

         Server server=new Server();
         server.start();
    }
    //1.多线程CS类：处理多个客户端连接的线程类---读入存入线程当中
    //使用内部类原因：多线程在Server中可以随时使用--读入方法
    //问题：一个客户连接服务器，需要流操作： 一个客户端与一个服务器端说话【卡住】
    //把服务器端写--放入线程中--目的：来一个客户端---配上服务器写
     private class CS implements Runnable{
       //3.定义私有属性：给当前多线程类使用
         private  Socket socket;//返回一个客户端通讯：socket
        //4.多个客户端--交给CS构造器确定--那个客户端--进行读入操作
         public CS(Socket s){
            //5.通过线路创建--传入socket--确定就是当前线程socket
             this.socket=s;
        }
        //2.服务端功能：读入--加入线程中
         public void run() {
             BufferedReader br = null;
             try {
             InputStream is = socket.getInputStream();
             InputStreamReader isr =
                     new InputStreamReader(is, "UTF8");

            br = new BufferedReader(isr);
             String str = null;
             while (true) {
                str = br.readLine();
                System.out.println("客户端说的话：" + str);
            }
            }  catch (NullPointerException e) {
                 System.out.println("IO流传输对象，" +
                        "有对象为null,立马核对，" +
                        "建议：查看try块中对象可能赋值为null");
            }  catch (UnsupportedEncodingException e) {
                 System.out.println("客户端或服务器端：字符集不一致");
            }  catch (IOException e) {
                 System.out.println("通讯过程当中，传输出现错误！");
            }  finally {
                 if(br!=null)
                     try {
                        br.close();
                    }  catch (IOException e) {
                        System.out.println("结束通讯：关闭失败！");
                    }
                if  (socket!= null)
                    try  {
                        socket.close();
                    }  catch (IOException e) {
                        System.out.println("通讯关闭失败");
                    }

            }

        }
    }
}

